package info.javatech.jmf.client.controller;

import info.javatech.jmf.client.controller.exceptions.ComponentConstructionException;
import info.javatech.jmf.client.controller.wsclient.PComponent;
import info.javatech.jmf.client.controller.wsclient.PEvent;
import info.javatech.jmf.client.controller.wsclient.PRequest;
import info.javatech.jmf.client.controller.wsclient.PRequestProcessException;
import info.javatech.jmf.client.controller.wsclient.PResponse;
import info.javatech.jmf.client.controller.wsclient.WSFrontend;
import info.javatech.jmf.client.controller.wsclient.WSFrontend_Stub;
import info.javatech.jmf.client.util.ComponentRegistry;
import info.javatech.jmf.common.ProtocolConstants;
import java.rmi.RemoteException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Item;
import javax.microedition.lcdui.ItemCommandListener;
import javax.microedition.lcdui.ItemStateListener;

/**
 *
 * @author afedotov
 */
public class JMFClientController implements ItemCommandListener,ItemStateListener,CommandListener{
    
    /**
     * JMF application midlet;
     */
    private JMFMidlet midlet;
    
    /**
     * JMF ws frontend handler
     */
    private WSFrontend wsFrontend;
    
    /**
     * viewId of current displayed page
     */
    private String currentViewId;
    
    /**
     * sessionId of current session, null if session is undefined
     */
    private String currentSessionId;
    
    private String currentViewState;
    
    /**
     * Stores wrapperClassName/wrapperInstance
     */
    private Hashtable wrappers;
    
    /**
     *Stores the list of currentViewComponents
     */
    private Vector components;
    
    /** Creates a new instance of JMFClientController */
    public JMFClientController(JMFMidlet midlet) {
        
        components = new Vector(0);
        
        this.midlet = midlet;
        
        wsFrontend = new WSFrontend_Stub();
        
        String[] wrapperTypes = ComponentRegistry.getWrapperTypes();
        
        wrappers = new Hashtable(wrapperTypes.length);
        
        for(int i = 0;i<wrapperTypes.length;i++) {
            try {
                JMFAbstractComponentWrapper wrapper = (JMFAbstractComponentWrapper) Class.forName(wrapperTypes[i]).newInstance();
                wrapper.setController(this);
                
                wrappers.put(wrapperTypes[i],wrapper);
            } catch (InstantiationException ex) {
                ex.printStackTrace();
            } catch (ClassNotFoundException ex) {
                ex.printStackTrace();
            } catch (IllegalAccessException ex) {
                ex.printStackTrace();
            }
        }
    }
    
    public void doInitialRequest() {
        PRequest initialRequest = new PRequest();
        
        initialRequest.setSessionId(null);
        initialRequest.setViewId("/server-wrapper/testStub.jsf");
        initialRequest.setViewState(null);
        initialRequest.setComponents(new PComponent[]{});
        initialRequest.setEvent(null);
        PResponse response = null;
        try {
            response = wsFrontend.doRequest(initialRequest);
        } catch (RemoteException ex) {
            ex.printStackTrace();
        } catch (PRequestProcessException ex) {
            ex.printStackTrace();
        }
        this.currentSessionId = response.getSessionId();
        this.currentViewId = response.getViewId();
        this.currentViewState = response.getViewState();
        
        Displayable disp = constructDisplayable(response);
        Display.getDisplay(midlet).setCurrent(disp);
    }
    
    /**
     *Construct PRequest object, based on state of controller and components
     */
    private PRequest constructRequest(PEvent event, PComponent eventSource) {
        PRequest req = new PRequest();
        
        req.setViewId(currentViewId);
        req.setSessionId(currentSessionId);
        req.setViewState(currentViewState);
        
        PComponent[] comps = new PComponent[components.size()];
        
        for(int i = 0;i<components.size();i++) {
            Object j2meComponent= components.elementAt(i);
            JMFComponentWrapperInterface wrapper = (JMFComponentWrapperInterface) wrappers.get(ComponentRegistry.getWrapperClassByJ2meComponent(j2meComponent));
            comps[i] = wrapper.persist(j2meComponent,eventSource);
        }
        
        req.setComponents(comps);
        
        return req;
    }
    
    private Displayable constructDisplayable(PResponse resp) {
        
        try {
            Displayable disp = null;
            
            PComponent scr = null;
            
            if (resp.getViewType()==ProtocolConstants.ROOT_COMPONENT_KIND_SCREEN) {
                scr = resp.getScreen();
            }else{
                scr = resp.getScreenContainer();
            }
            
            JMFComponentWrapperInterface wrapper = (JMFComponentWrapperInterface)wrappers.get(ComponentRegistry.getWrapperClassNameByComponent(scr));
            disp = (Displayable) wrapper.constructInstance(scr);
            
            return disp;
        } catch (ComponentConstructionException ex) {
            ex.printStackTrace();
        }
        
        // TODO this is a temporary solution until appropriate handlers for exceptions will be implemented
        return null;
    }
    
    private void doRequest(PEvent event, PComponent eventSource){
        PRequest req = constructRequest(event, eventSource);
        PResponse response = null;
        try {
            response = wsFrontend.doRequest(req);
        } catch (RemoteException ex) {
            ex.printStackTrace();
        } catch (PRequestProcessException ex) {
            ex.printStackTrace();
        }
        
        currentViewId = response.getViewId();
        currentSessionId = response.getSessionId();
        currentViewState = response.getViewState();
        
        this.components.removeAllElements();
        
        for(Enumeration en = wrappers.keys();en.hasMoreElements();) {
            ((JMFComponentWrapperInterface)wrappers.get(en.nextElement())).beforeNewResponse();
        }
        
        Displayable disp = constructDisplayable(response);
        
        Display.getDisplay(midlet).setCurrent(disp);
    }
    
    public void commandAction(Command command, Item item) {
        JMFComponentWrapperInterface wrapper = (JMFComponentWrapperInterface) wrappers.get(ComponentRegistry.getWrapperClassByJ2meComponent(item));
        PEvent event = wrapper.handleEvent(null,item,command);
        if (event!=null) doRequest(event,event.getComponent());
    }
    
    public void itemStateChanged(Item item) {
        JMFComponentWrapperInterface wrapper = (JMFComponentWrapperInterface) wrappers.get(ComponentRegistry.getWrapperClassByJ2meComponent(item));
        PEvent event = wrapper.handleEvent(null,item,null);
        if (event!=null) doRequest(event,event.getComponent());
    }
    
    public void commandAction(Command command, Displayable displayable) {
        JMFComponentWrapperInterface wrapper = (JMFComponentWrapperInterface) wrappers.get(ComponentRegistry.getWrapperClassByJ2meComponent(displayable));
        PEvent event = wrapper.handleEvent(displayable,null,command);
        if (event!=null) doRequest(event,event.getComponent());
    }
    
    public PComponent askToPersist(Object obj) {
        JMFAbstractComponentWrapper wrapper = (JMFAbstractComponentWrapper) wrappers.get(ComponentRegistry.getWrapperClassByJ2meComponent(obj));
        return wrapper.persist(obj,null);
    }
    
    public Object askToConstruct(PComponent component) throws ComponentConstructionException {
        JMFAbstractComponentWrapper wrapper = (JMFAbstractComponentWrapper) wrappers.get(ComponentRegistry.getWrapperClassNameByComponent(component));
        return wrapper.constructInstance(component);
    }
    
    public void registerComponent(Object component) {
        components.addElement(component);
    }
    
}
